#include "BF_Lib.h"
#include "HDF_Lib.h"
#include "FMM_Lib.h"
#include "BMM_Lib.h"
#include <stdlib.h>
#include <string.h>


/* Ρουτινα που ελέγχει αν το blockNum είναι έγκυρο. */

BOOLEAN BlockIsValid ( int blockNum );

void BF_Init ( void )
{
	FMM_Init();
	BMM_Init();
}

int BF_CreateFile ( char *fileName )
{
	int error;
	char header[BF_BLOCK_SIZE];
	FILE *file;

	if ( HDF_ExistsFile ( fileName ) == TRUE)
	{
		BF_Errno = BFE_FILEEXISTS;
		return BFE_FILEEXISTS;
	}
	
	error = HDF_CreateFile( fileName );

	if ( error < 0)
	{
		BF_Errno = error;
		return error;
	}	
	
	/* Αρχικοποίηση του πρώτο block του αρχείου, το οποίο
	 *θα αποτελεί το header του αρχείου. 
	 */

	memset ( header, 0,sizeof( BF_BLOCK_SIZE) );
	error = HDF_OpenFile ( fileName, &file );

	if ( error < 0 )
	{
		BF_Errno = error;
		return error;
	}

	/* Εγγραφή του header στο αρχείο. */

	error = HDF_WriteBlock ( 0, header, file );

	if ( error < 0 )
	{
		BF_Errno = error;
		return error;
	}

	error = HDF_CloseFile ( file );

	if ( error < 0 )
	{
		BF_Errno = error;
		return error;
	}


	BF_Errno = BFE_OK;
	return BFE_OK;
}

int BF_DestroyFile ( char *fileName )
{
	int error;

	if ( HDF_ExistsFile ( fileName ) == FALSE )
	{
		BF_Errno = BFE_FILEEXISTS;
		return BFE_FILEEXISTS;
	}
	
	if ( FMM_IsOpen( fileName ) == TRUE )
	{
		BF_Errno = BFE_CANNOTDESTROYFILE;
		return BFE_CANNOTDESTROYFILE;
	}

	/* Καθαρισμός της ενδιάμεσης μνήμης απο τα block του
	 * αρχείου που θα καταστραφεί.
	 */

	error = BMM_Clear ( fileName );
	
	if ( error < 0 )
	{
		BF_Errno = error;
		return error;
	}

	error = HDF_DestroyFile ( fileName );

	if ( error < 0 )
	{
		BF_Errno = error;
		return error;
	}


	BF_Errno = BFE_OK;
	return BFE_OK;
}

int BF_OpenFile ( char *fileName )
{
	FILE *file;
	int error, fd;

	if ( HDF_ExistsFile ( fileName ) == FALSE )
	{
		BF_Errno = BFE_FILEEXISTS;
		return BFE_FILEEXISTS;
	}

	fd = FMM_Open ( fileName );

	if ( fd < 0 )
	{
		BF_Errno = fd;
		return fd;
	}

	BF_Errno = BFE_OK;
	return fd;
}

int BF_CloseFile ( int fileDesc )
{
	fileInfo_t fileInfo;
	int error;

	if ( FMM_IsValid ( fileDesc ) == FALSE)
	{
		BF_Errno = BFE_FD;
		return BFE_FD;
	}

	fileInfo.fd = fileDesc;
	error = FMM_GetFileInfoByFD ( &fileInfo );
	
	if ( error < 0 )
	{
		BF_Errno = error;
		return error;
	}

	/* Εγγραφή των βρώμικων block που έχουν απομείνει
	 *στην ενδιάμεση μνήμη.
	 */

	error = BMM_Flush ( fileInfo );
	
	if ( error < 0 )
	{
		BF_Errno= error;
		return error;
	}

	error = FMM_Close ( fileDesc );

	if ( error < 0 )
	{
		BF_Errno = error;
		return error;
	}

	BF_Errno = BFE_OK;
	return BFE_OK;
}

int BF_GetFirstBlock ( int fileDesc, int *blockNum, char **blockBuf )
{
	int error;
	fileInfo_t fileInfo;

	if ( FMM_IsValid( fileDesc ) == FALSE)
	{
		BF_Errno = BFE_FD;
		return BFE_FD;
	}

	/* Προσκόμηση του πρώτου έγκυρου block του αρχείου. */

	*blockNum = FMM_HEAD_GetFirstBlock ( fileDesc );

	if ( blockNum < 0 )
	{
		BF_Errno = *blockNum;
		return *blockNum;
	}
	
	fileInfo.fd = fileDesc;
	error = FMM_GetFileInfoByFD ( &fileInfo ); 
	
	if ( error < 0 )
	{
		BF_Errno  = error;
		return error;
	}
	 
	/* Προσκόμηησ του block στην ενδιάμεση μνήμη. */

	error = BMM_GetBlock ( fileInfo, *blockNum, blockBuf );

	if ( error < 0 )
	{
		BF_Errno = error;
		return error;
	}

	BF_Errno = BFE_OK;
	return BFE_OK;
}

int BF_GetNextBlock ( int fileDesc, int *blockNum, char **blockBuf )
{
	int error;
	fileInfo_t fileInfo;

	if ( FMM_IsValid( fileDesc) == FALSE)
	{
		BF_Errno = BFE_FD;
		return BFE_FD;
	}

	if ( BlockIsValid ( *blockNum ) == FALSE )
	{
		BF_Errno = BFE_INVALIDBLOCK;
		return BFE_INVALIDBLOCK;
	}

	/* Εύρεση του επόμενου έγκυρου block. */

	*blockNum = FMM_HEAD_GetNextBlock ( fileDesc, *blockNum);

	if ( *blockNum < 0 )
	{
		BF_Errno = *blockNum;
		return *blockNum;
	}

	fileInfo.fd = fileDesc;
	error = FMM_GetFileInfoByFD ( &fileInfo );

	if ( error < 0 )
	{
		BF_Errno = error;
		return error;
	}

	/* Προσκόμηση του blovk στη μνήμη. */

	error = BMM_GetBlock ( fileInfo, *blockNum, blockBuf );	

	if ( error < 0 )
	{
		BF_Errno = error;
		return error;
	}

	BF_Errno = BFE_OK;
	return BFE_OK;
}

int BF_GetThisBlock ( int fileDesc, int blockNum, char **blockBuf )
{
	int error;
	fileInfo_t fileInfo;

	if ( BlockIsValid ( blockNum ) == FALSE )
	{
		BF_Errno = BFE_INVALIDBLOCK;
		return BFE_INVALIDBLOCK;
	}

	if ( FMM_IsValid ( fileDesc ) == FALSE )
	{
		BF_Errno= BFE_FD;
		return BFE_FD;
	}

	if ( FMM_HEAD_IsValid ( fileDesc, blockNum ) == FALSE )
	{
		BF_Errno = BFE_INVALIDBLOCK;
		return BFE_INVALIDBLOCK;
	}


	fileInfo.fd = fileDesc;
	error = FMM_GetFileInfoByFD ( &fileInfo );

	if ( error < 0 )
	{
		BF_Errno = error;
		return error;
	}

	error = BMM_GetBlock ( fileInfo, blockNum, blockBuf );

	if ( error < 0 )
	{
		BF_Errno = error;
		return error;
	}

	BF_Errno = BFE_OK;
	return BFE_OK;
}

int BF_AllocBlock ( int fileDesc, int *blockNum, char **blockBuf )
{
	int error;
	fileInfo_t fileInfo;

	if ( FMM_IsValid ( fileDesc ) == FALSE )
	{
		BF_Errno = BFE_FD;
		return BFE_FD;
	}
	
	/* Προσκόμηση του πρώτου άκυρου block, είτε αυτό 
	 *πρέρχεται απο ανακύκλωση, είτε είναι το τελευταίο 
	 *φυσικό block του αρχείου.
	 */

	*blockNum = FMM_HEAD_GetFirstEmpty ( fileDesc );

	if ( *blockNum < 0 )
	{
		BF_Errno = *blockNum;
		return *blockNum;
	}

	/* Ενημέρωση του Header ότι το block έγινε έγκυρο. */

	error = FMM_HEAD_Set ( fileDesc, *blockNum, TRUE );

	if ( error < 0 )
	{
		BF_Errno = error;
		return error;
	}

	fileInfo.fd = fileDesc;
	error = FMM_GetFileInfoByFD ( &fileInfo );

	if ( error < 0 )
	{
		BF_Errno = error;
		return error;
	}

	error = BMM_AllocBlock ( fileInfo, *blockNum, blockBuf );
	
	if ( error < 0 )
	{
		BF_Errno = error;
		return error;
	}

	BF_Errno = BFE_OK;
	return BFE_OK;
}


int BF_DisposeBlock ( int fileDesc, int blockNum )
{
	int error;
	fileInfo_t fileInfo;
	
	if ( BlockIsValid ( blockNum ) == FALSE )
	{
		BF_Errno = BFE_INVALIDBLOCK;
		return BFE_INVALIDBLOCK;
	}

	if ( FMM_IsValid ( fileDesc ) == FALSE )
	{
		BF_Errno = BFE_FD;
		return BFE_FD;
	}
	
	if ( FMM_HEAD_IsValid ( fileDesc, blockNum ) == FALSE)
	{
		BF_Errno = BFE_INVALIDBLOCK;
		return BFE_INVALIDBLOCK;
	}

	fileInfo.fd = fileDesc;
	error = FMM_GetFileInfoByFD ( &fileInfo );

	if ( error < 0 )
	{
		BF_Errno = error;
		return error;
	}

	/* Πέταγμα του block απο την ενδιάμεση μνήμη. */

	error = BMM_DisposeBlock ( fileInfo, blockNum );
	
	if ( error < 0 )
	{
		BF_Errno = error;
		return error;
	}

	/* Ενημέρωση της κεφαλίδαςοτι το block αυτό έγινε άκυρο. */

	error = FMM_HEAD_Set ( fileDesc, blockNum, FALSE );

	if ( error < 0 )
	{
		BF_Errno = error;
		return error;
	}

	BF_Errno = BFE_OK;
	return BFE_OK;
}


int BF_UnpinBlock ( int fileDesc, int blockNum, int dirty )
{
	int error;
	fileInfo_t fileInfo;

	if ( BlockIsValid ( blockNum ) == FALSE )
	{
		BF_Errno = BFE_INVALIDBLOCK;
		return BFE_INVALIDBLOCK;
	}

	if ( FMM_IsValid ( fileDesc ) == FALSE )
	{
		BF_Errno = BFE_FD;
		return BFE_FD;
	}
	
	fileInfo.fd = fileDesc;
	error = FMM_GetFileInfoByFD ( &fileInfo );

	if ( error < 0 )
	{
		BF_Errno = error;
		return error;
	}

	/* Ξεπινάρισμα του block απ οτην ενδιάμεση μνήμη. */

	error = BMM_UnpinBlock ( fileInfo, blockNum, dirty );
	
	if ( error < 0 )
	{
		BF_Errno = error;
		return error;
	}

	BF_Errno = BFE_OK;
	return BFE_OK;
}


void BF_PrintError ( char * errString )
{   
	printf ( "%s ", errString );

    switch (BF_Errno)
	{
        case BFE_NOMEM:
            printf("%s  BFE_NOMEM\n", errString);
            break;        

        case BFE_CANNOTOPENFILE:
            printf("%s BFE_CANNOTOPENFILE\n", errString);
            break;        
            
        case BFE_CANNOTCLOSEFILE:
            printf("%s BFE_CANNOTCLOSEFILE\n", errString);
            break;        
            
        case BFE_CANNOTCREATEFILE:
            printf("%s BFE_CANNOTCREATEFILE\n", errString);
            break;        
            
        case BFE_INCOMPLETEREAD:
            printf("%s BFE_INCOMPLETEREAD\n", errString);
            break;        
            
        case BFE_INCOMPLETEWRITE:
            printf("%s BFE_INCOMPLETEWRITE\n", errString);
            break;        
            
        case BFE_FILEEXISTS:
            printf("%s BFE_FILEEXISTS\n", errString);
            break;        
            
        case BFE_NOBUF:
            printf("%s BFE_NOBUF\n", errString);
            break;        

		case BFE_LISTERROR:
            printf("%s BFE_LISTERROR\n", errString);
            break;        
            
        case BFE_FILEOPEN:
            printf("%s BFE_FILEOPEN\n", errString);
            break;        
            
        case BFE_FD:
            printf("%s BFE_FD\n", errString);
            break;        
            
        case BFE_FILENOTEXISTS:
            printf("%s BFE_FILENOTEXISTS\n", errString);
            break;        
            
        case BFE_FTABFULL:
            printf("%s BFE_FTABFULL\n", errString);
            break;        
            
        case BFE_HEADOVERFLOW:
            printf("%s BFE_HEADOVERFLOW\n", errString);
            break;        
            
        case BFE_BLOCKFIXED:
            printf("%s BFE_BLOCKFIXED\n", errString);
            break;        
            
        case BFE_BLOCKUNFIXED:
            printf("%s BFE_BLOCKUNFIXED\n", errString);
            break;        
            
        case BFE_EOF:
            printf("%s BFE_EOF\n", errString);
            break;        
            
        case BFE_FILEHASFIXEDBLOCKS:
            printf("%s BFE_FILEHASFIXEDBLOCKS\n", errString);
            break;        
            
        case BFE_BLOCKFREE:
            printf("%s BFE_BLOCKFREE\n", errString);
            break;        
            
        case BFE_BLOCKINBUF:
            printf("%s BFE_BLOCKINBUF\n", errString);
            break;        
            
        case BFE_BLOCKNOTINBUF:
            printf("%s BFE_BLOCKNOTINBUF\n", errString);
            break;        
            
        case BFE_INVALIDBLOCK:
            printf("%s BFE_INVALIDBLOCK\n", errString);
            break;        
            
        case BFE_CANNOTDESTROYFILE:
            printf("%s BFE_CANNOTDESTROYFILE\n", errString);
            break;        
    }
}


BOOLEAN BlockIsValid ( int blockNum )
{
	return blockNum >= 0 && blockNum < MAX_BLOCKS;
}

